package org.greatstorm.service;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.greatstorm.dao.ArticleDao;
import org.greatstorm.dao.CategoryDao;
import org.greatstorm.dao.ReplyDao;
import org.greatstorm.dao.TagArticleRelationDao;
import org.greatstorm.dao.TagDao;
import org.greatstorm.entity.Article;
import org.greatstorm.entity.Category;
import org.greatstorm.entity.EntityObject;
import org.greatstorm.entity.Reply;
import org.greatstorm.entity.Tag;
import org.greatstorm.entity.TagArticleRelation;
import org.greatstorm.tx.Transaction;
import org.greatstorm.tx.TransactionManager;
import com.google.inject.Inject;
import com.google.inject.Singleton;

@Singleton
public class ArticleManager {
	@Inject
	private ArticleDao articleDao;
	@Inject
	private CategoryDao categoryDao;
	@Inject
	private TagDao tagDao;
	@Inject
	private ReplyDao replyDao;
	@Inject
	private TagArticleRelationDao atRelationDao;

	@SuppressWarnings("unchecked")
	public String addArticle(String categoryKey, String articleTitle,
			String articleContent, List<String> tagNames) {

		EntityObject article, category;

		article = new EntityObject();
		article.put(Article.ARTICLE_TITLE, articleTitle);
		article.put(Article.CONTENT, articleContent);

		Transaction tx = TransactionManager.getTransaction();
		tx.beginTransaction();

		category = categoryDao.getEntityByKey(categoryKey);
		article.put(Article.CATEGORY_FK, category.getKey());
		article.put(Article.TAGS, tagNames);
		article.put(Article.READ_COUNT, 0);
		article.put(Article.STICKY, 0);
		article.put(Article.LOCK, 0);
		articleDao.save(article);

		// if tag exits, update, otherwise, add it
		for (String tagName : tagNames) {
			EntityObject tagObj = tagDao.getTagByName(tagName);

			// not exist, add it
			if (tagObj == null) {
				EntityObject t = new EntityObject();
				t.put(Tag.TAG_NAME, tagName);
				tagDao.save(t);

				// save the relation
				EntityObject r = new EntityObject();
				r.put(TagArticleRelation.ARTICLE_KEY, article.getKey());
				r.put(TagArticleRelation.TAG_KEY, t.getKey());
				atRelationDao.save(r);
			} else {
				// exists, save the relation
				EntityObject r = new EntityObject();
				r.put(TagArticleRelation.ARTICLE_KEY, article.getKey());
				r.put(TagArticleRelation.TAG_KEY, tagObj.getKey());
				atRelationDao.save(r);
			}
		}

		int count = (Integer) category.get(Category.ARTICLE_COUNT);
		category.put(Category.ARTICLE_COUNT, count + 1);
		categoryDao.update(category);

		tx.commit();

		return article.getKey();
	}

	@SuppressWarnings("unchecked")
	public Map<String, Object> getArticleById(String id) {
		Map<String, Object> rt = new HashMap<String, Object>();

		// 1. get article object
		EntityObject article = articleDao.getEntityByKey(id);
		if (article != null) {
			rt.put(Article.KIND, article);
		}

		// 2. get article category
		EntityObject cat = categoryDao.getEntityByKey((String) article
				.get(Article.CATEGORY_FK));
		if (cat != null) {
			rt.put(Category.KIND, cat);
		}

		// 3. get article tags
		List<EntityObject> tags = new ArrayList<EntityObject>();
		for (String tagName : (List<String>) article.get(Article.TAGS)) {
			EntityObject tag = tagDao.getTagByName(tagName);

			if (tag != null) {
				tags.add(tag);
			}
		}
		rt.put(Tag.KIND, tags);

		// 4. get reply
		List<EntityObject> replies = replyDao.getReplyListByArticleKey(article
				.getKey());
		rt.put(Reply.KIND, replies);

		return rt;
	}

	public void deleteAll() {
		articleDao.deleteAll();
	}

	public List<EntityObject> getArticleByCategory(int pageno, String key) {

		// TODO: decide the page size from configueration
		int pagesize = 10;
		return articleDao.getArticleByCategory(pageno, pagesize, key);
	}

	public List<EntityObject> getAllArticle(Integer pageno) {
		int pagesize = 10;
		return articleDao.getAllArticle(pageno, pagesize);
	}

	public String deleteArticleByKey(String key) {
		EntityObject o = articleDao.getEntityByKey(key);
		articleDao.delete(key);

		return (String) o.get(Article.ARTICLE_TITLE);
	}

	public void stickyArticle(String key, int optVal) {
		EntityObject o = articleDao.getEntityByKey(key);
		o.put(Article.STICKY, optVal);
		articleDao.update(o);
	}

	public void lockArticle(String key, int optVal) {
		EntityObject o = articleDao.getEntityByKey(key);
		o.put(Article.LOCK, optVal);
		articleDao.update(o);
	}

	@SuppressWarnings("unchecked")
	public void editArticle(String articleKey, String categoryKey,
			String title, String content, List<String> tagNames) {
		Transaction tx = TransactionManager.getTransaction();
		tx.beginTransaction();

		EntityObject oldArticle = articleDao.getEntityByKey(articleKey);
		int count = 0;
		EntityObject article, category;

		// update old category
		String oldCatKey = (String) oldArticle.get(Article.CATEGORY_FK);
		if (!oldArticle.get(Article.CATEGORY_FK).equals(categoryKey)) {
			EntityObject oldCat = categoryDao.getEntityByKey(oldCatKey);
			count = (Integer) oldCat.get(Category.ARTICLE_COUNT);
			oldCat.put(Category.ARTICLE_COUNT, count - 1);
			categoryDao.update(oldCat);

			category = categoryDao.getEntityByKey(categoryKey);
			count = (Integer) category.get(Category.ARTICLE_COUNT);
			category.put(Category.ARTICLE_COUNT, count + 1);
			categoryDao.update(category);
		}

		// update old tags:
		atRelationDao.removeRelationByArticleKey(oldArticle.getKey());

		article = articleDao.getEntityByKey(articleKey);
		article.put(Article.ARTICLE_TITLE, title);
		article.put(Article.CONTENT, content);
		article.put(Article.TAGS, tagNames);
		articleDao.update(article);

		// if tag exits, update, otherwise, add it
		for (String tagName : tagNames) {
			EntityObject tagObj = tagDao.getTagByName(tagName);

			// not exist, add it
			if (tagObj == null) {
				EntityObject t = new EntityObject();
				t.put(Tag.TAG_NAME, tagName);
				tagDao.save(t);

				// save the relation
				EntityObject r = new EntityObject();
				r.put(TagArticleRelation.ARTICLE_KEY, article.getKey());
				r.put(TagArticleRelation.TAG_KEY, t.getKey());
				atRelationDao.save(r);
			} else {
				// exists, save the relation
				EntityObject r = new EntityObject();
				r.put(TagArticleRelation.ARTICLE_KEY, article.getKey());
				r.put(TagArticleRelation.TAG_KEY, tagObj.getKey());
				atRelationDao.save(r);
			}
		}

		tx.commit();
	}
}
